home *** CD-ROM | disk | FTP | other *** search
/ Gigarom 1 / Gigarom Macintosh Archives (Quantum Leap)(CDRM1080320)(1993).iso / FILES / MID / Util / MIDI.a.cpt / midi.a next >
Text File  |  1987-03-26  |  18KB  |  704 lines

  1. *    MIDI.a - assembly language routines callable from MPW C
  2. *
  3. * The following routines are provided for communicating with a MIDI interface
  4. * through the Macintosh's serial ports. If you want to call them from
  5. * MPW Pascal, simply declare them with the EXTERNAL; C; directives.
  6. * NOTE NOTE NOTE: You MUST put this module in a segment that remains fixed for
  7. * the duration of the program (like ARes in a MacApp program). These routines
  8. * mess with the interrupts, and contain code that can be jumped to 
  9. * asynchronously.
  10. *
  11. *    SCCInitA(clockRate,txBuffer,rxBuffer)    -Initialize port A to
  12. *    int clockRate;             communicate with MIDI.
  13. *    Ptr txBuffer,rxBuffer;
  14. *
  15. *    SCCInitB(clockRate,txBuffer,rxBuffer)    -Initialize port B to
  16. *    int clockRate;             communicate with MIDI.
  17. *    Ptr txBuffer,rxBuffer;
  18. *
  19. *    TxMIDI(port,outByte)    -Transmit one byte to MIDI port
  20. *    int port,outByte;
  21. *
  22. *    int
  23. *    RxMIDI(port)    -Recieve one byte from MIDI port (or -1 if none)
  24. *    int port;
  25. *
  26. *    Boolean
  27. *    MIDIBusy(port)    -Returns true if still transmitting, else false
  28. *    int port;
  29. *
  30. *    MIDIEnable(port,mode)    -Sets MIDI mode: enabled, disabled,
  31. *    int port,mode;         echoA or echoB
  32. *
  33. *    SCCRstA()        -Resets SCC and restores interrupts
  34. *
  35. *    SCCRstB()        -Resets SCC and restores interrupts
  36.  
  37.     CASE    OBJECT    ;Communicate with C
  38.     OPT    NOCLR
  39.  
  40. * Fetch the following equates for serial chip addresses and offsets
  41. * from SysEqu.a
  42. *
  43. * SCCRd        SCC base read address [pointer]
  44. * SCCWr        SCC base write address [pointer]
  45. * Lvl2DT        Interrupt level 2 dispatch table [32 bytes]
  46. * aData        offset for A channel data
  47. * aCtl        offset for A channel control
  48. * bData        offset for B channel data
  49. * bCtl        offset for B channel control
  50. * txBE        SCC transmit buffer empty
  51. * CurrentA5    current value of A5 [pointer]
  52.  
  53.     Include    'SysEqu.a'
  54.     Include    'Traps.a'
  55.  
  56. Lvl2Int        EQU    $0200
  57. TxAIntOffset    EQU    16
  58. RxAIntOffset    EQU    24
  59. SACondOffset    EQU    28
  60. TxBIntOffset    EQU    0
  61. RxBIntOffset    EQU    8
  62. SBCondOffset    EQU    12
  63.  
  64. PortA        EQU    0
  65. PortB        EQU    1
  66.  
  67. Disabled        EQU    0
  68. EchoA        EQU    1
  69. EchoB        EQU    2
  70. Enabled        EQU    3
  71.  
  72. * Macro for silly do-nothing instruction that allows the SCC chip
  73. * to settle down
  74.  
  75.     MACRO
  76.     delay    
  77.     move.l    (sp),(sp)
  78.     ENDM
  79.  
  80. * Macro for writing SCC control ports, assuming a0
  81. * is already set up to point to them
  82.  
  83.     MACRO
  84.     ToScc    ®Ptr,&message
  85.     move.b    ®Ptr,(a0)    ;pointer for SCC reg
  86.     delay
  87.     move.b    &message,(a0)    ;send byte to SCC reg
  88.     delay
  89.     ENDM
  90.  
  91. * The following record template, Q (for queue, get it?) is a very important
  92. * data structure in this set of routines. It completely defines a data queue,
  93. * including housekeeping variables and the SCC addresses pertaining to the
  94. * queue. Each port can have a transmit queue and a receive queue, so there
  95. * may be four of these data structures in all. The total size is dependent
  96. * on the size of the non-relocatable blocks delivered in the SCCInit routines,
  97. * where the queues are initialized.
  98.  
  99. Q:    RECORD    {Queue}
  100. Empty    ds.b    1    ;1 if queue is empty, 0 otherwise
  101. Enable    ds.b    1    ;0:disabled,3:enabled,1:echo to A, 2:echo to B
  102. ByteIn    ds.w    1    ;index to next cell in
  103. ByteOut    ds.w    1    ;index to next cell out
  104. Size    ds.w    1    ;size of following queue
  105. CtlAddr    ds.l    1    ;address of control register for this queue
  106. DataAddr    ds.l    1    ;address of data register for this queue
  107. OldInt    ds.l    1    ;old interrupt vector
  108. Queue    ds.b    1    ;but it can be as big as you want
  109.     ENDR
  110.  
  111. Globals:    RECORD    ;--------------------MIDI Globals---------------------
  112.  
  113. ChnReset        ds.w    1    ;SCC channel reset select
  114. OldSACondInt    ds.l    1
  115. OldSBCondInt    ds.l    1
  116.  
  117. TxAQueue        ds.l    1    ;transmitted data queue,port A
  118. RxAQueue        ds.l    1    ;received data queue, port A
  119. TxBQueue        ds.l    1    ;transmitted data queue,port B
  120. RxBQueue        ds.l    1    ;received data queue, port B
  121.  
  122.  
  123.     ENDR
  124.  
  125.     ENTRY (RxAIntHand,RxBIntHand):CODE
  126.     ENTRY (TxAIntHand,TxBIntHand):CODE
  127.     ENTRY (SACondHand,SBCondHand):CODE
  128.  
  129.     PROC
  130.  
  131.     EXPORT    (SCCInitA,SCCInitB):CODE
  132.     BRANCH    SHORT
  133.     WITH    Globals
  134.  
  135.  
  136. * These are the initialization routines which should be called
  137. * when you initialize quickdraw and the various managers
  138.  
  139. * Call SCCInitA to use the modem port, and SCCInitB to use the printer port
  140. * The calling sequence is:
  141. *        SCCInitA(clockRate,txBuffer,rxBuffer)
  142. *    or    SccInitB(clockRate,txBuffer,rxBuffer)
  143. *     where:
  144. *
  145. *    clockRate    is an integer; the legal values are 500,
  146. *        1000, or 2000, depending on whether the external clock
  147. *        provided by the MIDI interface is 500kHz, 1MHz, or 2MHz.
  148. *
  149. *    txBuffer    is the address of a buffer for the transmitted data;
  150. *        this must be a pointer to a non-relocatable block provided
  151. *        by the Memory Manager. It should be of sufficient size to
  152. *        handle the biggest data block that you might want to send
  153. *        at one time; remember that the Macintosh can fill up the
  154. *        transmission buffer much more quickly than it can be
  155. *        transmitted at the MIDI baud rate.
  156. *
  157. *    rxBuffer    is the address of a buffer for the received data; comments
  158. *        that were made about txBuffer apply to this buffer also.
  159. * Note: registers d0,a0,a1 are not preserved - they need not be if called
  160. * from MPW C
  161.  
  162. Frame    RECORD    {LinkReg}
  163. LinkReg    ds.l    1
  164. Return    ds.l    1
  165. clockRate    ds.l    1
  166. TxBuffer    ds.l    1
  167. RxBuffer    ds.l    1
  168.     ENDR
  169.  
  170.     WITH Frame
  171.  
  172.     MACRO
  173.     SetUpQ    &port,&io        ;&port = A/B; &io = Tx/Rx
  174.     
  175.     ; Set up queue address (it must point at the first location
  176.     ; after all of the houskeeping variables, which are kept at
  177.     ; the beginning of the non-relocatable block
  178.     move.l    &io.Buffer(a6),a0    ;pointer to non-reloc. block in a0
  179.     _GetPtrSize        ;leaves size in d0
  180.     sub.l    #Q,a0        ;a0 points to beginning of queue
  181.     move.l    a0,&io&port.Queue    ;save queue pointer
  182.     add.w    #Q,d0        ;Q is a negative offset
  183.     move.w    d0,Q.Size(a0)
  184.     move.l    &io&port.Queue,r&io.Queue
  185.  
  186.     ;Now initalize the queue housekeeping variables
  187.     IF &io = 'Tx' THEN
  188.     move.l    SCCWr,a1
  189.     ELSE
  190.     move.l    SCCRd,a1
  191.     ENDIF
  192.  
  193.     move.w    #&port.Ctl,d0
  194.     lea    (a1,d0),a0
  195.     move.l    a0,Q.CtlAddr(r&io.Queue)
  196.     move.w    #&port.Data,d0
  197.     lea    (a1,d0),a0
  198.     move.l    a0,Q.DataAddr(r&io.Queue)
  199.     clr    Q.ByteIn(r&io.Queue)    ;init flags and pointers
  200.     clr    Q.ByteOut(r&io.Queue)
  201.     move.b    #$ff,Q.Empty(r&io.Queue)
  202.     clr.b    Q.Enable(r&io.Queue)
  203.  
  204.     ; Initialize interrupt vectors
  205.     move.l    #Lvl2DT,a0        ;get dispatch table pointer
  206.     move.l    &io&port.IntOffset(a0),Q.OldInt(r&io.Queue)
  207.     lea    &io&port.IntHand,a1
  208.     move.l    a1,&io&port.IntOffset(a0)
  209.  
  210.     IF &io = 'Rx' THEN ;only do this once
  211.     lea    S&port.CondHand,a1
  212.     move.l    S&port.CondOffset(a0),OldS&port.CondInt ;save old address
  213.     move.l    a1,S&port.CondOffset(a0)
  214.     ENDIF
  215.     
  216.     ENDM
  217.  
  218. * Define some mnemonic names for the registers we use to address the queues
  219.  
  220. rTxQueue    EQU    a2
  221. rRxQueue    EQU    a3
  222.  
  223. SCCInitA:
  224.     link    a6,#0
  225.     movem.l    rTxQueue/rRxQueue,-(sp)
  226.     move    sr,-(sp)            ;Save interrupts
  227.     ori    #Lvl2Int,sr        ;Disable interrupts
  228.  
  229.     SetUpQ    A,Tx
  230.     SetUpQ    A,Rx
  231.     move.b    #%10000000,ChnReset
  232.     bra.w    SCCInit
  233.  
  234. SCCInitB:
  235.     link    a6,#0
  236.     movem.l    rTxQueue/rRxQueue,-(sp)
  237.     move    sr,-(sp)            ;Save interrupts
  238.     ori    #Lvl2Int,sr        ;Disable interrupts
  239.  
  240.     SetUpQ    B,Tx
  241.     SetUpQ    B,Rx
  242.     move.b    #%01000000,ChnReset
  243.  
  244. SCCInit:
  245.     move.l    Q.CtlAddr(rRxQueue),a0
  246.     move.b    (a0),d0            ;dummy read
  247.     delay
  248.     move.l    Q.CtlAddr(rTxQueue),a0
  249.  
  250.  
  251.     ;Reset channel
  252.     ToScc    #9,ChnReset    
  253.  
  254. ;This is where you determine the external clock rate
  255.  
  256. ;%01000100 = 500 kHz
  257. ;%10000100 = 1 MHz
  258. ;%11000100 = 2 MHz
  259.  
  260.     cmp.l    #500,clockRate(a6)    ;is the argument 500kHz?
  261.     bne    clk2000        ;no, try 2MHz
  262.     ToScc    #4, #%01000100    ;16x clock, 1 stop bit
  263.     bra    endclk
  264. clk2000:    cmp.l    #2000,clockRate(a6)
  265.     bne    clk1000        ;no, assume 1Mhz
  266.     ToScc    #4, #%11000100    ;64x clock, 1 stop bit
  267.     bra    endclk
  268. clk1000:    ToScc    #4, #%10000100    ;32x clock, 1 stop bit
  269. endclk:    ToScc    #1, #$00        ;No W/Req
  270.     ToScc    #3, #$00        ;Turn off Rx
  271.     ToScc    #5, #$00        ;Turn off Tx
  272.     ToScc    #9, #$00        ;*Master interrupt disable
  273.     ToScc    #11,#$28        ;Make TRxC clock source
  274.     ToScc    #14,#$00        ;Disable BRGen
  275.     ToScc    #3, #$c1        ;Enable Rx
  276.     ToScc    #5, #$6a        ;Enable Tx and drivers
  277.     ToScc    #0, #$80        ;*Reset TxCRC
  278.     ToScc    #15,#$08        ;Enable DCD int for mouse
  279.     ToScc    #0, #$10        ;Reset EXT/STATUS
  280.     ToScc    #0, #$10        ;Reset EXT/STATUS
  281.     ToScc    #1, #$13        ;Enable interrupts
  282.     ToScc    #9, #$0a        ;Set master int enable
  283.  
  284.     move    (sp)+,sr        ;Restore interrupts
  285.     movem.l    (sp)+,rTxQueue/rRxQueue
  286.     unlk    a6        ;Restore link register
  287.     rts            ;and return
  288.     
  289.     ENDWITH
  290.     ENDPROC
  291.  
  292. *
  293. * This is the MPW C subroutine to transmit a MIDI byte of data.
  294. * Calling convention:
  295. *    TxMIDI(port,outByte);
  296. * Note: Registers d0,a0,a1 are not preserved; they need not be
  297. * if called from MPW C
  298. *
  299.  
  300. TxMIDI    PROC    EXPORT
  301.  
  302. Frame    RECORD    {LinkReg}
  303. LinkReg    ds.l    1
  304. Return    ds.l    1
  305. port    ds.l    1    ;MIDI port (0 = A, 1 = B)
  306. outByte    ds.l    1    ;Data byte to be transmitted (least sig. byte)
  307.     ENDR
  308.  
  309.     WITH    Globals
  310.     WITH    Frame
  311.     link    a6,#0        ;set frame pointer
  312.     move.l    a2,-(sp)
  313.     move    sr,-(sp)        ;save interrupts
  314.     ori    #Lvl2Int,sr    ;disable interrupts
  315.  
  316.  
  317.     move.l    TxAQueue,a0    ;in case port = A
  318.     move.l    RxAQueue,a2
  319.     tst.l    port(a6)
  320.     beq    @3
  321.     move.l    TxBQueue,a0
  322.     move.l    RxBQueue,a2
  323. @3
  324.     tst.b    Q.Empty(a0)    ;is TxQueue empty?
  325.     bne    @TxQE        ;if so, branch
  326.     move    Q.ByteIn(a0),d0    ;if not, add byte to queue
  327.     move.l    outByte(a6),d1    ;get longword containing data byte
  328.     move.b    d1,(a0,d0)    ;place byte in queue
  329.     addq    #1,d0        ;update TxByteIn
  330.     cmp    Q.Size(a0),d0
  331.     bne    @1
  332.     clr    d0
  333. @1
  334.     move    d0,Q.ByteIn(a0)
  335.     bra    @Exit        ;and exit
  336.  
  337. @TxQE:
  338.     move.l    Q.CtlAddr(a2),a1    ;get SCC Read Control Address
  339.     btst.b    #txBE,(a1)    ;transmit buffer empty?
  340.     bne    @FirstByte    ;if so, branch
  341.     move    Q.ByteIn(a0),d0    ;if not, add to queue
  342.     move.l    outByte(a6),d1    ;get longword containing data byte
  343.     move.b    d1,(a0,d0)    ;place byte in queue
  344.     addq    #1,d0        ;update index
  345.     cmp    Q.Size(a0),d0
  346.     bne    @2
  347.     clr    d0
  348. @2
  349.     move    d0,Q.ByteIn(a0)
  350.     clr.b    Q.Empty(a0)    ;reset queue empty flag
  351.     bra    @Exit        ;and exit
  352.  
  353. @FirstByte:
  354.     move.l    Q.DataAddr(a0),a1    ;get SCC Write Data address
  355.     delay
  356.     move.l    outByte(a6),d1    ;get longword containing data byte
  357.     move.b    d1,(a1)        ;write data to SCC
  358.     delay
  359. @Exit:
  360.     move    (sp)+,sr        ;restore interrupts
  361.     move.l    (sp)+,a2
  362.     unlk    a6        ;restore frame pointer
  363.     rts
  364.  
  365.     ENDWITH
  366.     ENDWITH
  367.  
  368.     ENDPROC
  369.  
  370.  
  371.  
  372. *
  373. * This routine receives a byte of MIDI data. It returns an int data type;
  374. * The byte is actually returned in the lower half of the returned int.
  375. * Calling convention:
  376. *    inByte = RxMIDI(port);
  377. * If no byte is available at the time the routine is called, -1 is returned.
  378. * Note: registers d0,d1, and a1 are not preserved, as they need not be if
  379. * called by MPW C.
  380. *
  381.  
  382. RxMIDI    FUNC    EXPORT
  383.  
  384. Frame    RECORD    {LinkReg}
  385. LinkReg    ds.l    1
  386. Return    ds.l    1
  387. port    ds.l    1    ;MIDI port (0 = A, 1 = B)
  388.     ENDR
  389.  
  390.     WITH    Globals
  391.     WITH    Frame
  392.     link    a6,#0        ;set frame pointer
  393.     move    sr,-(sp)        ;save interrupts
  394.     ori    #Lvl2Int,sr    ;disable interrupts
  395.  
  396.     move.l    RxAQueue,a0    ;in case port = A
  397.     tst.l    port(a6)
  398.     beq    @3
  399.     move.l    RxBQueue,a0
  400. @3
  401.     tst.b    Q.Empty(a0)    ;any data available?
  402.     beq    @1        ;if so, branch
  403.     move.l    #-1,d0        ;if not, return -1
  404.     bra    @Exit
  405. @1
  406.     move    Q.ByteOut(a0),d1    ;get index to byte out
  407.     clr.l    d0        ;return data in d0
  408.     move.b    Q.Queue(a0,d1),d0    ;get MIDI data
  409.     addq    #1,d1        ;update index
  410.     cmp.w    Q.Size(a0),d1
  411.     bne    @2
  412.     clr.w    d1
  413. @2
  414.     move.w    d1,Q.ByteOut(a0)
  415.     cmp.w    Q.ByteIn(a0),d1    ;is queue empty?
  416.     bne    @Exit        ;if not, exit
  417.     move.b    #$ff,Q.Empty(a0)    ;if empty, set flag
  418.  
  419. @Exit:
  420.     move    (sp)+,sr        ;restore interrupts
  421.     unlk    a6        ;restore frame pointer
  422.     rts            ;return
  423.  
  424.     ENDWITH
  425.     ENDWITH
  426.     ENDFUNC
  427.  
  428. MIDIBusy    FUNC    EXPORT
  429.  
  430. * This routine informs the program of the status of the transmit queue; it
  431. * returns TRUE if the queue is not empty and FALSE if it is. The main purpose
  432. * of this routine is to allow the calling program to check whether it is safe
  433. * to call SCCRstA or SCCRstB.
  434. *    Boolean MIDIBusy(port);
  435. *    int port;
  436.  
  437. Frame    RECORD    {LinkReg}
  438. LinkReg    ds.l    1
  439. Return    ds.l    1
  440. port    ds.l    1    ;MIDI port (0 = A, 1 = B)
  441.     ENDR
  442.  
  443.     WITH    Globals
  444.     WITH    Frame
  445.     
  446.     link    a6,#0        ;set frame pointer
  447.     move.l    #-1,d0        ;set true (i.e. busy)
  448.     lea    RxAQueue,a0    ;in case this is port A
  449.     lea    TxAQueue,a1
  450.     tst.b    port(a6)
  451.     beq    @1
  452.     lea    RxBQueue,a0
  453.     lea    TxBQueue,a1
  454.  
  455. @1    tst.b    Q.Empty(a1)    ;is queue empty
  456.     beq    @Exit        ;if not, d0 is correct
  457.  
  458.     ;if we reach this point, the Tx queue is empty, but the SCC
  459.     ;might still be busy transmitting a byte. Let's check.
  460.  
  461.     move.l    Q.CtlAddr(a0),a1
  462.     btst.b    #txBE,(a1)    ;transmit buffer empty?
  463.     beq    @Exit        ;if not, d0 is correct
  464.     clr.l    d0        ;otherwise, set false
  465. @Exit:    unlk    a6        ;restore frame pointer
  466.     rts
  467.  
  468.     ENDWITH
  469.     ENDWITH
  470.     ENDFUNC
  471.  
  472. ;The following routine sets the RxEnable flag; there are four modes in which
  473. ;a receive queue may be, Disabled (=0), Enabled, EchoA and EchoB. In the
  474. ;disabled state all received MIDI bytes are thrown away. in the enabled state
  475. ;they are placed in a queue for reading by RxMIDI. In the two echo modes,
  476. ;the received bytes are never returned to RxMIDI (i.e. they are not put in
  477. ;the queue), but they are automatically echoed either to the A or B port.
  478. ;Note: disabling the port has the effect of throwing away any bytes that may
  479. ;still remain in the receive queue.
  480. ;    MIDIEnable(port,mode);
  481. ;    int port,mode;
  482.  
  483. MIDIEnable    PROC    EXPORT
  484.  
  485. Frame    RECORD    {LinkReg}
  486. LinkReg    ds.l    1
  487. Return    ds.l    1
  488. port    ds.l    1    ;MIDI port (0 = A, 1 = B)
  489. mode    ds.l    1
  490.     ENDR
  491.  
  492.     WITH    Globals
  493.     WITH    Frame
  494.     
  495.     link    a6,#0
  496.     move.l    RxAQueue,a0    ;in case port = PortA
  497.     tst.l    port(a6)
  498.     beq    @1
  499.     move.l    RxBQueue,a0
  500.  
  501. @1    move.l    mode(a6),d0
  502.     move.b    d0,Q.Enable(a0)
  503.     tst.b    d0
  504.     bne    @Exit        ;if not disabled, our work is done
  505.                 ;otherwise, clear queue
  506.     move    sr,-(sp)        ;save interrupts
  507.     ori    #Lvl2Int,sr    ;disable interrupts
  508.     clr    Q.ByteIn(a0)    ;reset Rx queue variables
  509.     clr    Q.ByteOut(a0)
  510.     move.b    #$ff,Q.Empty(a0)
  511.     move    (sp)+,sr        ;restore interrupts
  512. @Exit
  513.     unlk    a6
  514.     rts
  515.  
  516.     ENDWITH
  517.     ENDWITH
  518.     ENDPROC
  519.  
  520.  
  521. ;Note: In the following interrupt handling routines, the registers
  522. ;a0-a3 and d0-d3 are not preserved, since the Mac interrupt handler
  523. ;does this for us before calling these routines (see Inside Macintosh I-200)
  524.  
  525. ;This is the interrupt routine for receiving a byte of MIDI data.  It
  526. ;places the received byte in a circular queue to be accessed later
  527. ;by the application.
  528.  
  529.     PROC
  530.  
  531.     ENTRY (RxAIntHand,RxBIntHand):CODE
  532.     ENTRY (TxAIntHand,TxBIntHand):CODE
  533.     ENTRY (SACondHand,SBCondHand):CODE
  534.  
  535.     WITH Globals
  536.  
  537. RxAIntHand:
  538.     move.l    a5,-(sp)        ;Might not be pointing to App. Globals    
  539.     move.l    CurrentA5,a5    ;so we can access globals
  540.  
  541.     move.l    RxAQueue,a2
  542.     bra    RxIntHand
  543.  
  544. RxBIntHand:
  545.     move.l    a5,-(sp)        ;Might not be pointing to App. Globals    
  546.     move.l    CurrentA5,a5    ;so we can access globals
  547.  
  548.     move.l    RxBQueue,a2
  549.  
  550. RxIntHand:
  551.     ;Remember: a0 points to control read, data read is at offset 4
  552.     clr.l    d1
  553.     move.b    4(a0),d1        ;read data from SCC
  554.     delay
  555.  
  556.     tst.b    Q.Enable(a2)    ;is receive disabled?
  557.     beq    @Exit        ;if so, simply throw away byte and go
  558.  
  559.     cmp.b    #EchoA,Q.Enable(a2)    ;do we echo to port A?
  560.     bne    @TestB
  561.     ;call TxMIDI
  562.     move.l    d1,-(sp)        ;push byte
  563.     move.l    #PortA,-(sp)    ;push port number
  564.     bsr.w    TxMIDI
  565.     addq.l    #8,sp        ;pop parameters
  566.     bra @Exit
  567.  
  568. @TestB    cmp.b    #EchoB,Q.Enable(a2)    ;do we echo to port B?
  569.     bne    @QueueUp        ;if not, must save in queue
  570.     ;call TxMIDI
  571.     move.l    d1,-(sp)        ;push byte
  572.     move.l    #PortB,-(sp)    ;push port number
  573.     bsr.w    TxMIDI
  574.     addq.l    #8,sp        ;pop parameters
  575.     bra @Exit
  576.  
  577. @QueueUp    move    Q.ByteIn(a2),d0    ;get offset to next cell
  578.     move.b    d1,(a2,d0)    ;put byte in queue
  579.     clr.b    Q.Empty(a2)    ;reset queue empty flag
  580.     addq    #1,d0        ;update index
  581.     cmp.w    Q.Size(a2),d0
  582.     bne    @1
  583.     clr.w    d0
  584. @1    move.w    d0,Q.ByteIn(a2)
  585.  
  586. @Exit    move.l    (sp)+,a5
  587.     rts
  588.  
  589. ;This is the interrupt routine for transmitting a byte of MIDI data.
  590. ;It checks to see if there is any data to send.  If there is, it sends
  591. ;it to the SCC.  If there isn't, it resets the txBE interrupt in the
  592. ;SCC and exits.
  593.  
  594. TxAIntHand:
  595.     move.l    a5,-(sp)        ;Might not be pointing to App. Globals    
  596.     move.l    CurrentA5,a5    ;so we can access globals
  597.  
  598.     move.l    TxAQueue,a2
  599.     bra    TxIntHand
  600.  
  601. TxBIntHand:
  602.     move.l    a5,-(sp)        ;Might not be pointing to App. Globals    
  603.     move.l    CurrentA5,a5    ;so we can access globals
  604.  
  605.     move.l    TxBQueue,a2
  606.  
  607. TxIntHand:
  608.     tst.b    Q.Empty(a2)    ;is queue empty?
  609.     beq    @1        ;if not branch
  610.     ;Remember: a1 points to control write, data write is at offset 4
  611.     move.b    #$28,(a1)        ;if so, reset txBE interrupt
  612.     bra    @Exit        ;and exit
  613. @1    move.w    Q.ByteOut(a2),d0    ;get index to next data byte
  614.     move.b    (a2,d0),4(a1)    ;write data to SCC
  615.     addq    #1,d0        ;update index
  616.     cmp.w    Q.Size(a2),d0
  617.     bne    @2
  618.     clr.w    d0
  619. @2    move.w    d0,Q.ByteOut(a2)
  620.     move.w    Q.ByteIn(a2),d1
  621.     cmp.w    d0,d1        ;is TxQueue empty?
  622.     bne    @Exit        ;if not, exit
  623.     move.b    #$ff,Q.Empty(a2)    ;if so, set flag
  624.  
  625. @Exit:
  626.     move.l    (sp)+,a5
  627.     rts            ;and return
  628.  
  629. ; Special condition interrupt handler; resets the input and output ports
  630.  
  631. SACondHand:
  632.     move.l    SCCRd,a0
  633.     move    #aData,d0
  634.     tst.b    aData(a0)        ;clear Rx buffer
  635.     move.l    SCCWr,a0
  636.     move.b    #$30,aCtl(a0)    ;reset error
  637.     rts
  638.  
  639.  
  640. SBCondHand:
  641.     move.l    SCCRd,a0
  642.     tst.b    bData(a0)        ;clear Rx buffer
  643.     move.l    SCCWr,a0
  644.     move.b    #$30,bCtl(a0)    ;reset error
  645.     rts
  646.  
  647.     ENDWITH
  648.     ENDPROC
  649.  
  650. ;These routines must be called when the application quits or the system
  651. ;will crash due to the interrupt handling pointers becoming invalid.
  652.  
  653.     PROC
  654.     EXPORT (SCCRstA,SCCRstB):CODE
  655.  
  656.     WITH Globals
  657.  
  658. SCCRstA:
  659.     move    sr,-(sp)        ;save interrupts
  660.     ori    #$0300,sr        ;disable interrupts
  661.     move.b    #%10000000,ChnReset
  662.  
  663.     ;Restore old interrupts
  664.     move.l    #Lvl2DT,a0    ;get dispatch table pointer
  665.     move.l    OldSACondInt,SACondOffset(a0)
  666.     move.l    RxAQueue,a1
  667.     move.l    Q.OldInt(a1),RxAIntOffset(a0)
  668.     move.l    TxAQueue,a1
  669.     move.l    Q.OldInt(a1),TxAIntOffset(a0)
  670.     bra    SCCReset ;with register a1 pointing to TxQueue
  671.  
  672. SCCRstB:
  673.     move    sr,-(sp)        ;save interrupts
  674.     ori    #$0300,sr        ;disable interrupts
  675.     move.b    #%10000000,ChnReset
  676.  
  677.     ;Restore old interrupts
  678.     move.l    #Lvl2DT,a0    ;get dispatch table pointer
  679.     move.l    OldSBCondInt,SBCondOffset(a0)
  680.     move.l    RxBQueue,a1
  681.     move.l    Q.OldInt(a1),RxBIntOffset(a0)
  682.     move.l    TxBQueue,a1
  683.     move.l    Q.OldInt(a1),TxBIntOffset(a0)
  684.  
  685. SCCReset:
  686.  
  687.     move.l    Q.CtlAddr(a1),a0
  688.  
  689.     ToScc    #9,ChnReset    ;Reset channel
  690.     ToScc    #15,#$08        ;Enable DCD int
  691.     ToScc    #0, #$10        ;Reset EXT/STATUS
  692.     ToScc    #0, #$10        ;Reset EXT/STATUS
  693.     ToScc    #1, #$01        ;Enable mouse interrupts
  694.     ToScc    #9, #$0a        ;Set master int enable
  695.  
  696.     move    (sp)+,sr        ;restore interrupts
  697.     rts
  698.  
  699.  
  700.     ENDWITH
  701.     ENDPROC
  702.  
  703.     END
  704.